package winOrDie.graphics.maps;


import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import winOrDie.graphics.maps.Map_Cell_Editor.XML_ELEMENTS_Map_Cell;

/**
 * This public class defines different methods for load and save {@link Map_Board} data
 */
public final class Map_Board_Editor 
{

	/**
	 * This public static class defines the names of {@link Map_Board} XML elements.
	 */
	public static class XML_ELEMENTS_Map_Board
	{
		public static final String Element_Map_Board = "Map_Board";
		public static final String Attribute_Columns = "Columns";
		public static final String Attribute_Rows = "Rows";
		public static final String Element_Map_Board_Row = "Map_Board_Row";
	}

	/**
	 * This public static method allows to check correct columns and row from {@link Map_Board} passed as parameter.
	 * @param column
	 * @param row
	 * @param mapBoard
	 * @throws Exception
	 */
	public static void check_Column_And_Row(int column, int row, Map_Board mapBoard) throws Exception
	{
		if ((column < 0) || (row < 0))
		{
			Exception exception = new Exception("Column and row must be 0 or more.");
			throw exception;
		}
		
		if ((column > mapBoard.getColumns() - 1) || (row > mapBoard.getRows() -1))
		{
			Exception exception = new Exception("Column or row out of range.");
			throw exception;
		}
	}
	
	
	/**
	 * This public static method returns a {@link Element} object with row data of {@link Map_Board} object passed as parameter. Row 
	 * number also is indicated by parameter. The {@link Element} is created from {@link Document} object passed as parameter 
	 * @param row_Number
	 * @param mapBoard
	 * @param document
	 * @return {@link Element}
	 * @throws Exception
	 */
	private static Element get_Element_Row_From_Map_Board(
			int row_Number, 
			Map_Board mapBoard, 
			Document document
			) throws Exception
	{
		Element element_Map_Board_Row = document.createElement(XML_ELEMENTS_Map_Board.Element_Map_Board_Row);
		
		for (int column = 0; column < mapBoard.getColumns(); column++) 
		{
			try {
				element_Map_Board_Row.appendChild(
						mapBoard.getMapCell_Matrix()[column][row_Number].get_XML_Element(document)
					);
			} catch (Exception e) {
				Exception exception = new Exception("Error at column " + column + ". " + e.getMessage());
				throw exception;
			}
		}
		
		return element_Map_Board_Row;
	}
	
	/**
	 * This public static method returns a {@link Element} object with the data of {@link Map_Board} object passed as parameter.
	 * The {@link Element} is created from {@link Document} object passed as parameter 
	 * @param mapBoard
	 * @param document
	 * @return {@link Element}
	 * @throws Exception
	 */
	public static Element get_Element_From_Map_Board(
			Map_Board mapBoard,
			Document document
			) throws Exception
	{
		Element element_Map_Board = document.createElement(XML_ELEMENTS_Map_Board.Element_Map_Board);
		
		element_Map_Board.setAttribute(
				XML_ELEMENTS_Map_Board.Attribute_Columns, 
				String.valueOf(mapBoard.getColumns())
			);
		
		element_Map_Board.setAttribute(
				XML_ELEMENTS_Map_Board.Attribute_Rows, 
				String.valueOf(mapBoard.getRows())
			);
		
		
		for (int row = 0; row < mapBoard.getRows(); row++) {
			try {
				element_Map_Board.appendChild(
						get_Element_Row_From_Map_Board(row, mapBoard, document)
					);
			} catch (Exception e) {
				Exception exception = new Exception("Error at row " + row + ". " + e.getMessage());
				throw exception;
			}
		}
		
		return element_Map_Board;
	}
	
	
	/**
	 * This private static method returns a {@link Map_Cell} array with data read at {@link Element} passed as parameter. This data
	 * is the number of cells that are in that row, this number of cells is passed by columns parameter.
	 * @param element_Row
	 * @param columns
	 * @return {@link Map_Cell}[columns]
	 * @throws Exception
	 */
	private static final Map_Cell[] get_Map_Cell_Row_From_Element(
			Element element_Row,
			int columns
			) throws Exception
	{
		if (!element_Row.getNodeName().equals(XML_ELEMENTS_Map_Board.Element_Map_Board_Row))
		{
			Exception exception = new Exception("The element is not a Map_Board_Row element.");
			throw exception;
		}
		
		/*
		 * Check if number of columns passed as parameter is the same as Map_Cell elements
		 */
		NodeList nodeList_Cells = element_Row.getElementsByTagName(XML_ELEMENTS_Map_Cell.Element_Map_Cell);
		if (nodeList_Cells.getLength() != columns)
		{
			Exception exception = new Exception("Cells elements counted: (" + nodeList_Cells.getLength() + ") are different from columns atrribute (" + columns + ").");
			throw exception;
		}
		
		/*
		 * Load Map_Cell objects of the row
		 */
		Map_Cell mapCell_Row[] = new Map_Cell[columns];
		for (int column_Number = 0; column_Number < columns; column_Number++) {
			Element element_Map_Cell = (Element) nodeList_Cells.item(column_Number);
			mapCell_Row[column_Number] = new Map_Cell();
			mapCell_Row[column_Number].load_From_XML_Element(element_Map_Cell);
		}
		
		
		return mapCell_Row;
	}
	
	
	/**
	 * This public static method loads data of {@link Element} passed as parameter into returned {@link Map_Board} object.
	 * @param element
	 * @return {@link Map_Board}
	 * @throws Exception
	 */
	public static Map_Board get_Map_Board_From_Element (Element element) throws Exception
	{
		if (!element.getNodeName().equals(XML_ELEMENTS_Map_Board.Element_Map_Board))
		{
			Exception exception = new Exception("The element is not a Map_Board element.");
			throw exception;
		}
		
		/*
		 * Load and check columns and rows attributes	
		 */
		int columns = 0;
		int rows = 0;
		
		try {
			columns = Integer.valueOf(
					element.getAttribute(XML_ELEMENTS_Map_Board.Attribute_Columns)
					);
		} catch (Exception e) {
			Exception exception = new Exception("Error in columns attribute. " + e.getMessage());
			throw exception;
		}
		
		try {
			rows = Integer.valueOf(
					element.getAttribute(XML_ELEMENTS_Map_Board.Attribute_Rows)
					);
		} catch (Exception e) {
			Exception exception = new Exception("Error in rows attribute. " + e.getMessage());
			throw exception;
		}
	
		
		Map_Board_Factory.check_Columns_And_Rows_Number(columns, rows);

		
		/*
		 * Prepare Map_Board object and Check rows 
		 */
		
		Map_Board mapBoard = new Map_Board(columns, rows);
		
		final NodeList nodeList_rows = element.getElementsByTagName(XML_ELEMENTS_Map_Board.Element_Map_Board_Row);
		
		if (nodeList_rows.getLength() != rows)
		{
			Exception exception = new Exception("Rows elements counted: (" + nodeList_rows.getLength() + ") are different from rows atrribute (" + rows + ").");
			throw exception;
		}
		
		/*
		 * Prepare Map_Cell rows and read data from XML elements.
		 */
		Map_Cell mapCell_row[] = null;
		for (int node_Row_Number = 0; node_Row_Number < nodeList_rows.getLength(); node_Row_Number++) {
			Element element_row = (Element) nodeList_rows.item(node_Row_Number);
			mapCell_row = get_Map_Cell_Row_From_Element(element_row, columns);
			
			/*
			 * This trivial loop reorders and set the MapCell_Row array into Map_Board Map_Cell matrix  
			 */
			for (int current_Cell = 0; current_Cell < mapCell_row.length; current_Cell++) {
				mapBoard.set_Map_Cell(current_Cell, node_Row_Number, mapCell_row[current_Cell]);
				//mapBoard.getMapCell_Matrix()[current_Cell][node_Row_Number] = (Map_Cell) Array.get(mapCell_row, node_Row_Number);
			}
		}

		return mapBoard;
		
	}
	
}
